﻿using System.Data;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using DPLAB.IPAMS.DataLoader.Interface;
using NUnit.Framework;

namespace DPLAB.IPAMS.DataLoader.Excel
{
    public class DataMapper : IMapper
    {
        private readonly XPathDocument mappingDocument;
        private readonly XmlNamespaceManager namespaceManager;
        private readonly XPathNavigator navigator;

        public DataMapper(XPathDocument mappingDocument)
        {
            this.mappingDocument = mappingDocument;
            navigator = this.mappingDocument.CreateNavigator();
            namespaceManager = new XmlNamespaceManager(navigator.NameTable);
            namespaceManager.AddNamespace("map", "http://tempuri.org/Mapping.xsd");
        }

        #region IMapper Members

        public string MapTableName(DataTable table)
        {
            string originalTableName = table.TableName.Trim();
            string xpath =
                string.Format("/map:Map/map:Table[map:SourceName='{0}']", originalTableName);
            XPathNavigator translationNode = GetSingleNodeByXPath(xpath);
            if (translationNode == null)
                return originalTableName;
            XPathNavigator targetNameNode =
                translationNode.SelectSingleNode("map:TargetName", namespaceManager);
            if (targetNameNode == null)
                return originalTableName;
            return targetNameNode.Value;
        }

        public string FindOriginalTableName(DataTable table)
        {
            string mappedTableName = table.TableName.Trim();
            string xpath =
                string.Format(
                    "/map:Map/map:Table[map:SourceName='{0}' and not(map:TargetName) or map:TargetName='{0}']",
                    mappedTableName);
            XPathNavigator translationNode = GetSingleNodeByXPath(xpath);
            if (translationNode == null)
                return mappedTableName;
            return
                translationNode.SelectSingleNode("map:SourceName", namespaceManager).Value;
        }

        public string MapColumnName(DataColumn column)
        {
            XPathNavigator columnTranslationNode =
                GetSingleNodeByXPath(
                    string.Format(
                        "/map:Map/map:Table[map:SourceName='{0}']/map:Column[map:SourceName='{1}']/map:TargetName",
                        column.Table.TableName.Trim(), column.ColumnName.Trim()));
            if (columnTranslationNode == null)
                return column.ColumnName;
            else
                return columnTranslationNode.Value;
        }

        public string FindOriginalColumnName(DataColumn column)
        {
            XPathNavigator columnTranslationNode =
                GetSingleNodeByXPath(
                    string.Format(
                        "/map:Map/map:Table[map:SourceName='{0}']/map:Column[map:TargetName='{1}']/map:SourceName",
                        FindOriginalTableName(column.Table), column.ColumnName.Trim()));
            if (columnTranslationNode == null)
                return column.ColumnName;
            else
                return columnTranslationNode.Value;
        }

        #endregion

        private XPathNavigator GetSingleNodeByXPath(string xpath)
        {
            XPathExpression tableXPath =
                navigator.Compile(
                    xpath);
            tableXPath.SetContext(namespaceManager);
            return navigator.SelectSingleNode(tableXPath);
        }
    }

    [TestFixture]
    public class DataMapperTest
    {
        private static DataMapper GetMapper(string mappingXML)
        {
            var reader = new StringReader(mappingXML);
            var document = new XPathDocument(reader);
            return new DataMapper(document);
        }

        [Test]
        public void MapColumnName_ColumnHasMapping_ReturnTranslatedName()
        {
            DataMapper mapper =
                GetMapper(
                    "<Map xmlns=\"http://tempuri.org/Mapping.xsd\"><Table><SourceName>Table</SourceName><TargetName>TranslatedTable</TargetName><Column><SourceName>ColumnA</SourceName><TargetName>NewColumnName</TargetName></Column></Table></Map>");
            string tableName = "Table";
            var table = new DataTable(tableName);
            DataColumn column = table.Columns.Add("ColumnA");
            Assert.AreEqual("NewColumnName", mapper.MapColumnName(column));
        }

        [Test]
        public void MapColumnName_EmptyMapping_ReturnSameName()
        {
            DataMapper mapper =
                GetMapper("<Map xmlns=\"http://tempuri.org/Mapping.xsd\"/>");
            string tableName = "Table";
            var table = new DataTable(tableName);
            string columnName = "ColumnA";
            DataColumn column = table.Columns.Add(columnName);
            Assert.AreEqual(columnName, mapper.MapColumnName(column));
        }

        [Test]
        public void MapColumnName_TableHasMappingButNotColumn_ReturnSameName()
        {
            DataMapper mapper =
                GetMapper(
                    "<Map xmlns=\"http://tempuri.org/Mapping.xsd\"><Table><SourceName>Table</SourceName><TargetName>TranslatedTable</TargetName></Table></Map>");
            string tableName = "Table";
            var table = new DataTable(tableName);
            string columnName = "ColumnA";
            DataColumn column = table.Columns.Add(columnName);
            Assert.AreEqual(columnName, mapper.MapColumnName(column));
        }

        [Test]
        public void MapColumnName_TableHasNoMapping_ReturnSameName()
        {
            DataMapper mapper =
                GetMapper(
                    "<Map xmlns=\"http://tempuri.org/Mapping.xsd\"><Table><SourceName>OtherTable</SourceName></Table></Map>");
            string tableName = "Table";
            var table = new DataTable(tableName);
            string columnName = "ColumnA";
            DataColumn column = table.Columns.Add(columnName);
            Assert.AreEqual(columnName, mapper.MapColumnName(column));
        }

        [Test]
        public void
            MapTableName_TableWithNameThatAppearsWithoutTranslation_ReturnSameName()
        {
            DataMapper mapper =
                GetMapper(
                    "<Map xmlns=\"http://tempuri.org/Mapping.xsd\"><Table><SourceName>Table</SourceName></Table></Map>");
            string tableName = "Table";
            var table = new DataTable(tableName);
            Assert.AreEqual(tableName, mapper.MapTableName(table));
        }

        [Test]
        public void MapTableName_TableWithNameThatDoesntExist_ReturnSameName()
        {
            DataMapper mapper =
                GetMapper("<Map xmlns=\"http://tempuri.org/Mapping.xsd\"/>");
            string tableName = "Table";
            var table = new DataTable(tableName);
            Assert.AreEqual(tableName, mapper.MapTableName(table));
        }

        [Test]
        public void MapTableName_TableWithNameThatHasTranslation_ReturnTranslatedName()
        {
            DataMapper mapper =
                GetMapper(
                    "<Map xmlns=\"http://tempuri.org/Mapping.xsd\"><Table><SourceName>Table</SourceName><TargetName>TranslatedTable</TargetName></Table></Map>");
            var table = new DataTable("Table");
            Assert.AreEqual("TranslatedTable", mapper.MapTableName(table));
        }
    }
}